package io.github.openfeign.spring.enhancer;

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.NotFoundException;
import javassist.bytecode.BadBytecode;
import javassist.bytecode.Opcode;
import lombok.extern.slf4j.Slf4j;
/**
 * Apache HttpClient 字节码转换器 - 请求体处理增强
 *
 * <h3>核心改造：</h3>
 * <ul>
 *   <li>重构请求体处理逻辑，支持延迟实体创建</li>
 *   <li>实现以下关键修改：
 *     <ol>
 *       <li>定位请求体处理字节码位置</li>
 *       <li>替换传统字节数组实体为可延迟加载的消费者实体</li>
 *       <li>保持字符集编码兼容性</li>
 *     </ol>
 *   </li>
 * </ul>
 *
 * <h3>代码改造对比：</h3>
 *
 * <h4>原始实现（即时实体创建）:</h4>
 * <pre>{@code
 * if (request.body() != null) {
 *     // 立即创建StringEntity/ByteArrayEntity
 *     HttpEntity entity = createImmediateEntity(request);
 *     requestBuilder.setEntity(entity);
 * }
 * }</pre>
 *
 * <h4>优化后实现（延迟实体构造）:</h4>
 * <pre>{@code
 * if (hasDelayOutput(request)) {
 *     // 延迟实体创建
 *     requestBuilder.setEntity(new ConsumerHttpEntity(delayOutput));
 *     return requestBuilder.build();
 * }
 * if (request.body() != null) {
 *     // 保持原有处理逻辑
 *     HttpEntity entity = createImmediateEntity(request);
 *     requestBuilder.setEntity(entity);
 * }
 * }</pre>
 *
 * <h3>字节码关键位置：</h3>
 * <div class="bytecode">
 *   <pre>
 *   327: aload_1                         // 加载request对象
 *   328: invokevirtual #63              // 调用Request.body()
 *   331: ifnull        407              // 原始null检查跳转
 *   </pre>
 *   <p>改造后在此位置插入延迟输出检查逻辑，通过修改跳转地址实现处理分支</p>
 * </div>
 *
 * @see org.apache.http.client.methods.HttpRequestBase HTTP请求基类
 * @see org.apache.http.entity.ByteArrayEntity 被替换的字节数组实体
 */
@Slf4j
public class ApacheHttpClientBytecodeTransformer extends AbstractBytecodeTransformer {

    public ApacheHttpClientBytecodeTransformer(final ClassPool classPool, final String classOutPath) {
        super(classPool, classOutPath);
    }

    /**
     * {@inheritDoc}
     */
    public void transformBytecode() throws NotFoundException, CannotCompileException, BadBytecode {
        final CtClass ctClass = getCtClass();
        if (ctClass == null) {
            return;
        }
        if(log.isDebugEnabled()){
            log.debug("Start transforming feign.httpclient.ApacheHttpClient");
        }
        // 1. 正确获取方法参数类型
        final CtClass[] paramTypes = new CtClass[]{classPool.get("feign.Request"), classPool.get("feign.Request$Options")};
        // 2. 找出需要修改的方法
        final CtMethod method = ctClass.getDeclaredMethod("toHttpUriRequest", paramTypes);

        // 3. 定位到需要修改的行，插入逻辑
        insertCodeAtTargetMethod(method,
                "feign.Request",
                "body",
                "if ($1.requestTemplate().getDelayOutput() != null) {\n" +
                        "    requestBuilder.setEntity(new org.apache.http.entity.ConsumerHttpEntity($1.requestTemplate().getDelayOutput()));\n" +
                        "    return requestBuilder.build();\n" +
                        "}\n",
                Opcode.INVOKEVIRTUAL,
                AbstractBytecodeTransformer::locateIfNullInstruction);
        // 应用修改
        redefineClassInMemory(ctClass);

        if(log.isDebugEnabled()){
            log.debug("Successfully transforming feign.httpclient.ApacheHttpClient");
        }
    }

    private CtClass getCtClass() {
        try {
            return classPool.get("feign.httpclient.ApacheHttpClient");
        } catch (final NotFoundException e) {
            // 没有引入feign-form-spring，忽略
            log.warn("Class not found : feign.httpclient.ApacheHttpClient, feign-httpclient is not introduced. ", e);
        }
        return null;
    }
}
